package gtrans

/*just a FUNCTION that convert *sql.rows to custom struct
func NewTranslate(rows *sql.Rows) (trans *Translator)
func Translate[T any](trans *Translator) (result []*T, e error)

*/
import (
	"database/sql"
	"fmt"
	"reflect"
	"strings"
	"time"
)

// 中间字段
type MField struct {
	// 字段名
	name string
	//字段的数据库类型
	DType string
	//字段值
	Dvalue any
}

// 中间数据行
type MRow struct {
	//行的字段集合
	Fields []*MField
}

// 持有者 持有中间数据
type Translator struct {
	ready bool
	// 数据源 *sql.rows
	original *sql.Rows
	//中间数据行模板
	Template *MRow
	//中间行数据行
	Records    []*MRow
	nameFormat func(string) string
}

// u数据库字段名转换函数
// 转换后字符串用于与与泛型结构属性名进行比较
// 默认将数据库字段名首字母大写后进行比较
// 所有转换函数的结果首字母均应大写在go中首字母大写为导出属性反射后才可写
func (trans *Translator) SetNameCompare(dest func(string) string) {
	trans.nameFormat = dest
}

// 查找中间字段对象
func (mr *MRow) filedByName(name string) *MField {
	for _, v := range mr.Fields {
		if v.name == name {
			return v
		}
	}
	return nil
}

// 创建持有者并基础初始化
func NewTranslate(rows *sql.Rows) (trans *Translator) {
	trans = new(Translator)
	trans.ready = false
	trans.original = rows
	trans.Records = make([]*MRow, 0)
	trans.nameFormat = func(ori string) string {
		return strings.ToUpper(ori[:1]) + ori[1:]
	}
	return
}

// 初始化持有者 Translate时调用
func _init(trans *Translator) error {
	e := trans.createTemplateRowx()
	if e != nil {
		return fmt.Errorf("createtemp return error>%v", e)
	}
	return nil
}

// 创建中间数据行模版，由初始化时调用
func (trans *Translator) createTemplateRowx() (err error) {
	cols, e := trans.original.Columns()
	if e != nil {
		return fmt.Errorf("columns() error>%v", e)
	}
	trans.Template = createRow(cols)
	types, te := trans.original.ColumnTypes()
	if te != nil {
		return fmt.Errorf("columnTypes() error>%v", te)
	}
	trans.implantTypes(types)
	trans.ready = true
	err = nil
	return

}

// 创建中间数据行模板并初始化字段名
func createRow(cols []string) *MRow {
	lens := len(cols)
	row := new(MRow)
	row.Fields = make([]*MField, lens)
	for i, v := range cols {
		f := new(MField)
		f.name = v
		row.Fields[i] = f

	}
	return row
}

// 为行模板 初始化中间数据行模板的字段的数据库类型
func (trans Translator) implantTypes(types []*sql.ColumnType) {
	for _, t := range types {
		f := trans.Template.filedByName(t.Name())
		if f != nil {
			f.DType = t.DatabaseTypeName()
			fmt.Println(f)
		}
	}
}

// Translate 转换入口返回转换结果及其过程产生错误
// Parameter
// trans  持有者
// Return
// result 输出泛型类型指针的切片
// e 转换过程中出现的错误
// e=nil转换过程无错误
// e!=mil result 有nil 风险
func Translate[T any](trans *Translator) (result []*T, e error) {
	if !trans.ready {
		e := _init(trans)
		if e != nil {
			return nil, fmt.Errorf("_init error>%v", e)
		}

	}
	ep := parse[T](trans)
	if ep != nil {
		return nil, fmt.Errorf("Translate Parse error>%s ", ep)
	}
	return mapTo[T](trans)
}
func parse[T any](trans *Translator) (e error) {

	for trans.original.Next() {

		values, row := receiverMYSQL(trans.Template)
		if se := trans.original.Scan(values...); se != nil {
			e = fmt.Errorf("mParse scan error>%v", se)
			return
		}
		trans.Records = append(trans.Records, row)

	}

	return
}
func receiverMYSQL(template *MRow) (valuePtr []any, row *MRow) {
	result := make([]any, 0)
	newRow := new(MRow)
	newRow.Fields = make([]*MField, 0)
	for _, v := range template.Fields {
		field := new(MField)
		field.name = v.name
		field.DType = v.DType
		switch field.DType {
		case "VARCHAR", "CHAR", "TEXT":
			var str []byte
			field.Dvalue = str

			result = append(result, &field.Dvalue)
		case "TINYINT", "SMALLINT", "MEDIUMINT", "INT", "BIGINT":
			var i int64
			field.Dvalue = i
			result = append(result, &field.Dvalue)
		case "FLOAT", "DOUBLE":
			var i float64
			field.Dvalue = i
			result = append(result, &field.Dvalue)
		case "DATETIME", "TIMESTAMP":
			var t time.Time
			field.Dvalue = t
			result = append(result, &field.Dvalue)
		case "BLOB":
			var b = make([]byte, 0)
			field.Dvalue = b
			result = append(result, &v.Dvalue)
		default:
			continue
		}
		newRow.Fields = append(newRow.Fields, field)

	}
	return result, newRow

}
func mapTo[T any](trans *Translator) (result []*T, err error) {
	result = make([]*T, 0)
	err = nil
	for _, r := range trans.Records {

		v, e := mapSingle[T](trans, r)
		if e != nil {
			err = fmt.Errorf("single map error>%v", e)
			result = nil
			return

		}
		result = append(result, v)
	}
	return result, nil
}
func mapSingle[T any](trans *Translator, row *MRow) (obj *T, err error) {
	obj = new(T)
	// otype := reflect.TypeOf(*obj)
	oValue := reflect.ValueOf(obj).Elem()

	for _, value := range row.Fields {

		fieldName := trans.nameFormat(value.name)
		fieldValue := oValue.FieldByName(fieldName)
		isvalid, canSet := fieldValue.IsValid(), fieldValue.CanSet()
		if isvalid && canSet {
			switch fieldValue.Kind() {
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				v, ok := value.Dvalue.(int64)
				if ok {
					fieldValue.SetInt(v)
				}
			case reflect.String:
				v, ok := value.Dvalue.([]byte)
				if ok {
					fieldValue.SetString(string(v))
				}
			case reflect.Bool:
				v, ok := value.Dvalue.(bool)
				if ok {
					fieldValue.SetBool(v)
				}
			case reflect.Float32, reflect.Float64:
				v, ok := value.Dvalue.(float64)
				if ok {
					fieldValue.SetFloat(v)
				}
			// case reflect.Slice:
			// 	v, ok := value.Dvalue.([]byte)
			// 	if ok {
			// 		fieldValue.SetBytes(v)
			// 	}

			default:
				fmt.Println("the field ", fieldName, "  unmapped  to value base type it maybe map slice array")
				err = fmt.Errorf("unmatched field[%s]", fieldName)
				obj = nil
				return
			}

		} else {

			fmt.Println(fieldName, "Isvalid:", isvalid, "canSet:", canSet)
			continue
		}

	}
	return
}
